<?php

use Upfor\UpforPHP\Http\Request;
use Upfor\UpforPHP\Http\Response;
use Upfor\UpforPHP\Http\Router;
use Upfor\UpforPHP\Interfaces\ViewInterface;
use Upfor\UpforPHP\Library\ErrorLogger;
use Upfor\UpforPHP\Library\Juggler;
use Upfor\UpforPHP\Cache;
use Upfor\UpforPHP\Session;

/**
 * Get config data
 *
 * 对于固定的配置(静态定义, 或不区分环境的配置), 请放到Defines目录下, 参考ApiCode
 *
 * @param  string $item
 * @param  string $file
 * @return mixed
 */
function config($item = null, $file = null, $environment = null) {
    static $config = array();

    // Default config file
    if (empty($file)) {
        $file = 'config';
    }

    if (!isset($config[$file])) {
        $configFile = CONFIG_DIR . '/' . ENVIRONMENT . '/' . $file . '.php';
        if ($environment) {
            $configFile = CONFIG_DIR . '/' . $environment . '/' . $file . '.php';
        }

        if (!is_file($configFile)) {
            $configFile = CONFIG_DIR . '/' . $file . '.php';
        }

        if (!is_file($configFile)) {
            throw new InvalidArgumentException('Config file "' . $file . '.php" not exist');
        }

        $config[$file] = require $configFile;
    }

    if ($item) {
        if (isset($config[$file][$item])) {
            return $config[$file][$item];
        } else {
            return null;
        }
    } else {
        return $config[$file];
    }
}

/**
 * Cache
 *
 * @param  string  $key
 * @param  mixed   $value
 * @param  integer $ttl
 * @param  string  $driver
 * @return boolean
 */
function cache($key = null, $value = '', $ttl = null, $driver = null) {
    $cacheObj = Cache::driver($driver);
    if (is_null($key)) {
        return $cacheObj;
    } elseif (is_null($value)) {
        return $cacheObj->delete($key);
    } elseif ($value === '') {
        return $cacheObj->get($key, $value);
    } else {
        return $cacheObj->set($key, $value, $ttl);
    }
}

/**
 * Get cache driver
 *
 * @param  string|null $driver
 * @param  array|null  $config
 * @return mixed
 */
function cacheDriver($driver = null, $config = null) {
    static $hasSetDefault = false;
    if (!$hasSetDefault) {
        $hasSetDefault = true;

        if (config('defaultCache')) {
            Cache::setDefaultDriver(config('defaultCache'));
        }
    }

    return Cache::driver($driver, $config);
}

/**
 * Cookie manager
 *
 * @param  string|null $name   Cookie name
 * @param  mixed       $value  Cookie value
 * @param  mixed       $option Optional data
 * @return mixed
 */
function cookie($name, $value = '', $option = null) {
    if (is_null($name)) {
        // Clear all cookies
        response()->clearCookies();
    } elseif ('' === $value) {
        // Get one cookie or all cookies
        return request()->cookie($name);
    } elseif (is_null($value)) {
        // Delete cookie
        response()->deleteCookie($name);
    } else {
        // Set cookie
        $data['value'] = $value;

        if (!is_null($option)) {
            if (is_numeric($option)) {
                $data['expire'] = $option;
            } elseif (is_string($option)) {
                parse_str($option, $option);
                $data = array_merge($data, $option);
            } elseif (is_array($option)) {
                $data = array_merge($option, $data);
            }
        }

        response()->setCookie($name, $data);
    }

    return true;
}

/**
 * Session manager
 *
 * @param  string $name  Session name
 * @param  mixed  $value Session value
 * @return null|mixed|Session
 */
function session($name = '', $value = '') {
    static $sessionInstance;
    if (!($sessionInstance instanceof Session)) {
        $options = config('session');
        $sessionInstance = new Session($options);
    }

    if (is_null($name)) {
        // Clear session data
        $sessionInstance->clear();
    } elseif ('' === $name) {
        // Get session instance
        return $sessionInstance;
    } elseif ('' === $value) {
        // Get session data
        return $sessionInstance->get($name);
    } elseif (is_null($value)) {
        // Delete session data
        $sessionInstance->delete($name);
    } else {
        // Set a session
        $sessionInstance->set($name, $value);
    }

    return $sessionInstance;
}

/**
 * Get request instance
 *
 * @return Request
 */
function request() {
    static $instance;

    if (!$instance) {
        $instance = new Request();
    }

    return $instance;
}

/**
 * Get response instance
 *
 * @return Response
 */
function response() {
    static $instance;

    if (!$instance) {
        $instance = new Response();
    }

    return $instance;
}

/**
 * Get router instance
 *
 * @return Router
 */
function router() {
    static $instance;

    if (!$instance) {
        $instance = new Router(request());
    }

    return $instance;
}

/**
 * Get view instance
 *
 * @return ViewInterface
 */
function view() {
    static $instance;

    if (!($instance instanceof ViewInterface)) {
        $engine = isset(config('view')['engine']) ? config('view')['engine'] : 'Default';
        $engine = '\\Upfor\\UpforPHP\\View\\' . ucfirst($engine) . 'View';
        $instance = new $engine(isset(config('view')['config']) ? config('view')['config'] : []);
    }

    return $instance;
}

/**
 * Get database connection instance
 *
 * @param  string $dbName
 * @return Juggler
 * @throws ErrorException
 */
function db($dbName = null) {
    static $dbInstance = array();

    if (empty($dbName)) {
        $dbName = defaultDb();
    }

    if (!isset($dbInstance[$dbName])) {
        $dbList = config(null, 'database') ? : [];
        if (!isset($dbList[$dbName])) {
            throw new ErrorException("Database \"{$dbName}\" miss config");
        }

        $dbInstance[$dbName] = new Juggler($dbList[$dbName]);
    }

    return $dbInstance[$dbName];
}

/**
 * Get default database
 *
 * @return mixed
 * @throws ErrorException
 */
function defaultDb() {
    static $defaultDb = null;

    if (!$defaultDb) {
        $dbList = config(null, 'database') ? : [];
        if (config('defaultDb') && isset($dbList[config('defaultDb')])) {
            $defaultDb = config('defaultDb');
        } elseif ($dbList) {
            $defaultDb = array_shift($dbList);
        } else {
            $defaultDb = false;
        }
    }

    if ($defaultDb === false) {
        throw new ErrorException('No database configuration available');
    }

    return $defaultDb;
}

/**
 * Add a error log
 *
 * @param string       $level DEBUG|INFO|NOTICE|WARNING|ERROR|CRITICAL|ALERT|EMERGENCY
 * @param string|array $message
 * @param array        $context
 */
function logInfo($level, $message, array $context = array()) {
    if (!is_array($message)) {
        $message = array($message);
    }

    foreach ($message as $msg) {
        ErrorLogger::log($level, $msg, $context);
    }
}
